package com.lw.chat.client;

import com.lw.chat.protocol.MessageCodecSharable;
import com.lw.chat.protocol.ProtocolFrameDecoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import com.lw.chat.message.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

public class ChatClient {
    public static void main(String[] args) {
        NioEventLoopGroup worker = new NioEventLoopGroup();
        LoggingHandler loggingHandler = new LoggingHandler(LogLevel.DEBUG);
        MessageCodecSharable messageCodec = new MessageCodecSharable();

        CountDownLatch waitForLoginLatch = new CountDownLatch(1);//等待登录响应的锁
        AtomicBoolean loginFlag = new AtomicBoolean(false);//false登录失败

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(worker);
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new ProtocolFrameDecoder());
                    ch.pipeline().addLast(loggingHandler);
                    ch.pipeline().addLast(messageCodec);

                    /*
                    用来判断是不是读空闲时间过长或者写空闲时间过长，
                    参数一： 读空闲时间
                    参数二： 写空闲时间
                    参数三： 总共空闲时间
                    3秒如果没有朝服务器发送数据，触发 IdleState.WRITER_IDLE 事件
                     */
                    ch.pipeline().addLast(new IdleStateHandler(0,3,0));
                    ch.pipeline().addLast(new ChannelDuplexHandler(){ //双向处理器，入站、出站处理器

                        /**
                         * 用来触发特殊事件，比如 IdleState.WRITER_IDLE 事件
                         */
                        @Override
                        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                            if(evt instanceof  IdleStateEvent event){
                                if(event.state()== IdleState.WRITER_IDLE){//触发写空闲事件
                                    ctx.writeAndFlush(new PingMessage());
                                }
                            }
                        }
                    });

                    ch.pipeline().addLast("client handler", new ChannelInboundHandlerAdapter() {
                        /**
                         * 连接建立后触发active事件
                         */
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {

                            //构造消息对象
//                            LoginRequestMessage loginRequestMessage = new LoginRequestMessage("username", "password");
//                            //发送消息，触发出站操作，会调用messageCodec这个Handler的encode()方法
//                            ctx.writeAndFlush(loginRequestMessage);

                            new Thread(() -> {//负责接收用户在控制台的输入，向服务器发送消息
                                Scanner scanner = new Scanner(System.in);
                                System.out.println("输入用户名：");
                                String username = scanner.nextLine();
                                System.out.println("输入密码：");
                                String password = scanner.nextLine();

                                //构造消息对象
                                LoginRequestMessage loginRequestMessage = new LoginRequestMessage(username, password);
                                //发送消息，触发出站操作，会调用messageCodec这个Handler的encode()方法
//                                ctx.writeAndFlush(loginRequestMessage);
                                ctx.writeAndFlush(loginRequestMessage).addListener(future -> {
                                    if (future.isSuccess()) {
                                        System.out.println("Message sent successfully");
                                    } else {
                                        System.out.println("Failed to send message: " + future.cause());
                                    }
                                });

                                System.out.println("等待用户输入");
                                try {
                                    waitForLoginLatch.await();//阻塞线程
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                                if (!loginFlag.get()) {//登录失败
                                    ctx.channel().close();//会触发closeFuture().sync()，不再阻塞
                                    return;
                                }
                                while (true) {//后续功能
                                    System.out.println("=============================");
                                    System.out.println("send [username][content]");
                                    System.out.println("gsend [group name][content]");
                                    System.out.println("gcreate [group name][m1,m2,m3...]");
                                    System.out.println("gmembers [group name]");
                                    System.out.println("gjoin [group name]");
                                    System.out.println("gquit [group name]");
                                    System.out.println("quit");
                                    System.out.println("===============================");
                                    String command = scanner.nextLine();
                                    String[] commands = command.split(" ");
                                    switch (commands[0]){
                                        case "send" ->{ //send [username][content]
                                            Message message = new ChatRequestMessage(username, commands[1], commands[2]);
                                            ctx.writeAndFlush(message);
                                        }
                                        case "gsend" ->{//gsend [group name][content]
                                            Message message = new GroupChatRequestMessage(username, commands[1], commands[2]);
                                            ctx.writeAndFlush(message);
                                        }
                                        case "gcreate" ->{//gcreate [group name][m1,m2,m3...]
                                            String[] members = commands[2].split(",");
                                            List<String> memberList = Arrays.asList(members);
                                            Set<String> memberSet = new HashSet<>(memberList);
                                            memberSet.add(username);//加入自己
                                            Message message = new GroupCreateRequestMessage(commands[1], memberSet);
                                            ctx.writeAndFlush(message);
                                        }
                                        case "gmembers" ->{//gmembers [group name]
                                            Message message = new GroupMembersRequestMessage(commands[1]);
                                            ctx.writeAndFlush(message);
                                        }
                                        case "gjoin" ->{//gjoin [group name]
                                            Message message = new GroupJoinRequestMessage(username,commands[1]);
                                            ctx.writeAndFlush(message);
                                        }
                                        case "gquit" ->{//gquit [group name]
                                            Message message = new GroupQuitRequestMessage(username,commands[1]);
                                            ctx.writeAndFlush(message);
                                        }
                                        case "quit" ->{//quit
                                            ctx.channel().close();
                                            return;
                                        }
                                    }
                                }
                            }, "system in").start();
                        }

                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                            System.out.println("msg:" + msg);
                            if (msg instanceof LoginResponseMessage response) {
                                if (response.isSuccess()) {
                                    loginFlag.set(true);
                                }
                                waitForLoginLatch.countDown(); //计数器从1到0，唤醒system in 线程
                            }
                        }
                    });

                }
            });
            ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress("localhost", 8080)).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            worker.shutdownGracefully();
        }
    }
}
